#!/usr/bin/perl
use warnings;
use strict;
use Hash::Util qw( lock_keys unlock_keys
				   lock_value unlock_value
				   lock_hash unlock_hash
);

#******************************************************************************
# File: 
#		perl.pl
# Recipe: 5.3
#		Creating a Hash with Immutable Keys or Values
# Version:
#		0.1
# Author:
#   	Yubei Zhu
# Mail:
#		yubeizhu@gmail.com
# Created at:
#   	2012-08-17  
# Last Update:
#   	2012-08-17
# License: 
#		GPLv3	
#******************************************************************************

################################# Three types of lock ########################################
# 1. Key lock, support multiple
my %hTestHash = ( "TWO" => 2, "THREE" => 3);
# Recipe 5.1
$hTestHash{ ONE } = 1;

# Lock keys here
lock_keys( %hTestHash );
# Recipe 5.4
# We can still the keys before locked
delete $hTestHash{ ONE };

# Recipe 5.2  
if( not exists $hTestHash{ ONE } ){
	print "ONE deleted!\n";
}

# Recipe 5.6
printHash( \%hTestHash);

eval{
	$hTestHash{ FOUR } = 4;
};
print "Illegal operation when the hash is locked:\n$@" if $@;

# We can add the keys had been existed before the keys was locked
eval{
	$hTestHash{ ONE } = 1;
};
print "We can add the keys had been existed in hash before the keys were locked\n" if not $@;
printHash( \%hTestHash );

# Unlock keys now and insert new entry
unlock_keys( %hTestHash );
$hTestHash{ FOUR } = 4;
printHash( \%hTestHash );

# 2. Value lock, can only support single operation
# Lock Value, you can't modify the its value
# But after you delete it, the lock seems to be failed. 
lock_value( %hTestHash, "FOUR" );
eval{# We can modify the value here
	$hTestHash{ FOUR } = 5;
};
print "We can't modify the value of key 'FOUR', when it is locked:[$@]" if $@;
printHash(  \%hTestHash );

eval{# the lock on "FOUR" is expired.
	delete $hTestHash{ FOUR };
};
print "We can delete key 'FOUR' when it is locked." if not $@;

printHash(  \%hTestHash );

print "Lock expired!\n";
eval{# Add 'FOUR' again
	$hTestHash{ FOUR } = 5;
};
print "We can modify the value of key 'FOUR', when the lock is expired!" if not $@;
printHash(  \%hTestHash );

print "=====\n";
eval{
	$hTestHash{ FOUR } = 6;
};
print "We can modify the value of key 'FOUR' again, when the lock is expired!" if not $@;
printHash(  \%hTestHash );
unlock_value(%hTestHash,"FOUR");

# Lock the value where the key is not in the hash.
# It inserts the key with a value undef.
lock_value(%hTestHash,"TEN");
print "Lock a non-exixted key\n";
printHash(  \%hTestHash );
eval{
	$hTestHash{ TEN } = "10"
};
print "We can't insert the value of key 'TEN', when it is locked:[$@]" if $@;
unlock_value(%hTestHash,"TEN");

# 3. Lock entire hash
# We are locking the whole hash now
print "Testing of lock_hash\n";
lock_hash( %hTestHash );
eval{# We can't modify the value here
	$hTestHash{ ONE } = 5;
};
print "We can't modify the value of key 'ONE', when it is locked:[$@]" if $@;
eval{# Can't delete 
	delete $hTestHash{ ONE };
};
print "We can't delete key 'ONE' when the hash is locked:[$@]" if $@;
eval{# Can't insert
	$hTestHash{ ZERO } = 0 ;
};
print "We can't insert key 'ZERO' when the hash is locked:[$@]" if $@;
printHash(  \%hTestHash );
# Recover it
unlock_hash( %hTestHash );


sub printHash
#*****************************************************************************
# PURPOSE: 
#       print a hash
# USAGE:   
#       printHash( \%hAhash )
# PASSED ARGS:  
#       Hash reference
# RETURN ARGS:
#       None
# $sError - Fail Error Message
#       None
#******************************************************************************
{
	my ( $rhAHash ) = shift;
	if ( $rhAHash and ( ref($rhAHash) eq "HASH" ) ){
		print "+++++ Hash Begin\n";
		print map { "Key: [$_] and Value: [$rhAHash->{ $_ }]\n" } keys %{ $rhAHash };
		print "+++++ Hash End\n";
	}
}




